package level.editor.control.main;

import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JComponent;
import javax.swing.SwingUtilities;

public class DragScrollHandler extends MouseAdapter
{

	private JComponent component;
	private Point pressed, here;
	private Rectangle visiRect;

	public DragScrollHandler(JComponent component)
	{
		this.component = component;
		BUFFER_X = (int) component.getPreferredSize().getWidth() / 6;
		BUFFER_Y = (int) component.getPreferredSize().getHeight() / 6;
		component.addMouseListener(this);
		component.addMouseMotionListener(this);
	}

	public static void createDragScrollHandlerFor(JComponent component)
	{
		new DragScrollHandler(component);
	}

	public void dispose()
	{
		component.removeMouseListener(this);
		component.removeMouseMotionListener(this);
	}

	@Override
	public void mousePressed(MouseEvent e)
	{
		if (SwingUtilities.isRightMouseButton(e))
		{
			component.setCursor(new Cursor(Cursor.MOVE_CURSOR));
			pressed = e.getPoint();
			visiRect = component.getVisibleRect();
		}
	}

	@Override
	public void mouseReleased(MouseEvent e)
	{
		if (SwingUtilities.isRightMouseButton(e))
		{
			component.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
			if (resized)
			{
				((Main) component).minimapUpdate();
				resized = false;
			}
		}
	}

	@Override
	public void mouseDragged(MouseEvent e)
	{
		if (SwingUtilities.isRightMouseButton(e))
		{
			here = e.getPoint();
			visiRect.x += (pressed.x - here.x);
			visiRect.y += (pressed.y - here.y);
			component.scrollRectToVisible(visiRect);
			SwingUtilities.invokeLater(new Runnable()
			{
				public void run()
				{
					resize();
					Rectangle newRect = component.getVisibleRect();
					pressed.x += newRect.x - visiRect.x;
					pressed.y += newRect.y - visiRect.y;
					visiRect = newRect;
				}
			});
		}
	}

	private void resize()
	{
		Rectangle vis = component.getVisibleRect();
		Dimension size = component.getPreferredSize();
		Main main = (Main) component;
		// Expand right side

		if (vis.getX() + vis.getWidth() + BUFFER_X > size.getWidth())
		{
			main.setPreferredSize(new Dimension((int) (size.getWidth() + STEP),
					(int) size.getHeight()));
			resized = true;
		}

		// Expand left side

		if (vis.getX() < BUFFER_X)
		{
			main.setPreferredSize(new Dimension((int) (size.getWidth() + STEP),
					(int) size.getHeight()));
			main.scrollRectToVisible(new Rectangle((int) (vis.getX() + STEP),
					(int) vis.getY(), (int) vis.getWidth(), (int) vis
							.getHeight()));
			main.setOrigin(new Point((int) main.getOrigin().getX() + STEP,
					(int) main.getOrigin().getY()));
			resized = true;
		}

		// Compress right side

		if (vis.getX() + vis.getWidth() + 2 * BUFFER_X < size.getWidth())
		{
			main.setPreferredSize(new Dimension((int) (size.getWidth() - STEP),
					(int) size.getHeight()));
			resized = true;
		}

		// Compress left side

		if (vis.getX() > 2 * BUFFER_X)
		{
			main.setPreferredSize(new Dimension((int) (size.getWidth() - STEP),
					(int) size.getHeight()));
			main.scrollRectToVisible(new Rectangle((int) (vis.getX() - STEP),
					(int) vis.getY(), (int) vis.getWidth(), (int) vis
							.getHeight()));
			main.setOrigin(new Point((int) main.getOrigin().getX() - STEP,
					(int) main.getOrigin().getY()));
			resized = true;
		}

		// Expand top side

		if (vis.getY() <= BUFFER_Y)
		{
			main.setPreferredSize(new Dimension((int) size.getWidth(),
					(int) (size.getHeight() + STEP)));
			main.scrollRectToVisible(new Rectangle((int) vis.getX(), (int) (vis
					.getY() + STEP), (int) vis.getWidth(), (int) vis
					.getHeight()));
			main.setOrigin(new Point((int) main.getOrigin().getX(), (int) main
					.getOrigin().getY() + STEP));
			resized = true;
		}

		// Expand bottom side

		if (vis.getY() + vis.getHeight() + BUFFER_Y >= size.getHeight())
		{
			main.setPreferredSize(new Dimension((int) size.getWidth(),
					(int) (size.getHeight() + STEP)));
			resized = true;
		}

		// Compress top side

		if (vis.getY() > 2 * BUFFER_Y)
		{
			main.setPreferredSize(new Dimension((int) size.getWidth(),
					(int) (size.getHeight() - STEP)));
			main.scrollRectToVisible(new Rectangle((int) vis.getX(), (int) (vis
					.getY() - STEP), (int) vis.getWidth(), (int) vis
					.getHeight()));
			main.setOrigin(new Point((int) main.getOrigin().getX(), (int) main
					.getOrigin().getY() - STEP));
			resized = true;
		}

		// Compress bottom side

		if (vis.getY() + vis.getHeight() + 2 * BUFFER_Y < size.getHeight())
		{
			main.setPreferredSize(new Dimension((int) size.getWidth(),
					(int) (size.getHeight() - STEP)));
			resized = true;
		}
	}

	private int BUFFER_X = 1000;
	private int BUFFER_Y = 1000;
	private int STEP = 300;
	private boolean resized = false;
}
